package io.gitee.macxiang.annotation;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import io.gitee.macxiang._Enum.FieldStrategy;
import io.gitee.macxiang.entity.PathCode;
import io.gitee.macxiang.entity.SqlFieldMap;
import io.gitee.macxiang.utils.Datas;
import io.gitee.macxiang.utils.SQL;
import io.gitee.macxiang.utils.Times;

public class Reader {

  public static String getPathRoot(Class<?> clazz) {
    String t = clazz.getResource("").getPath();
    int i = t.indexOf("target/classes/");
    if (i < 0) {
      return null;
    }
    final String pathRoot = t.substring(0, i) + "src/main/java/";
    return pathRoot;
  }

  public static String getPackageService(Class<?> clazz) {
    final TableName tableName = clazz.getAnnotation(TableName.class);
    String t = clazz.getPackage().getName();
    final String packageService = String.format(tableName.servicePackage(), t.substring(0, t.lastIndexOf('.')));
    return packageService;
  }

  public static boolean isRelease(Class<?> clazz) {
    String t = String.format("%s%s.java", getPathRoot(clazz), clazz.getName().replace(".", "/"));
    final boolean release = Datas.fileExists(t, false) == null;
    return release;
  }

  private static String getClassName(Class<?> clazz) {
    return clazz.getSimpleName() + clazz.getAnnotation(TableName.class).serviceSuffix();
  }

  public static String getClassNameFull(Class<?> clazz) {
    return String.format("%s.%s", getPackageService(clazz), getClassName(clazz));
  }

  public static String getPathService(Class<?> clazz) {
    return String.format("%s%s.java", getPathRoot(clazz), getClassNameFull(clazz).replace(".", "/"));
  }

  public static String getPathAuto(String pathRoot, String packageService) {
    return String.format("%s%s/Auto.java",
        pathRoot,
        packageService.replace(".", "/"));
  }

  public static void writeAuto(String pathAuto, String packageService, StringBuilder content) {
    final String d = "package " + packageService + ";\n\n" + //
        "import io.gitee.macxiang.sql.wapper.BaseSet;\n" + //
        "import io.gitee.macxiang.sql.wapper.BaseWhere;\n" + //
        "import io.gitee.macxiang.sql.wapper.SetField;\n" + //
        "import io.gitee.macxiang.sql.wapper.WhereField;\n\n" + //
        "public class Auto {" + content + "\n}\n",
        s = Datas.readFileToString(pathAuto);
    if (s == null || !s.equals(d)) {
      Datas.writeFile(pathAuto, d);
      System.out.println("更新Auto文件:" + pathAuto);
    }
  }

  public static void genAuto(Class<?> clazz, StringBuilder sb, List<PathCode> list) {
    TableName tableName = clazz.getAnnotation(TableName.class);
    final String entityClassNameSimple = clazz.getSimpleName(),
        whereName = entityClassNameSimple + "Where",
        setName = entityClassNameSimple + "Set",
        pathService = getPathService(clazz),
        code = Datas.readFileToString(pathService),
        className = getClassName(clazz),
        packageService = getPackageService(clazz);
    String importEntity = "", entityClassName = clazz.getName();
    if (!className.equals(entityClassNameSimple)) {
      importEntity = "import " + entityClassName + ";\n";
      entityClassName = entityClassNameSimple;
    }
    StringBuilder content = new StringBuilder(
        String.format("\n  public static class %s extends BaseWhere {", whereName));
    Field[] fields = clazz.getDeclaredFields();
    Boolean notPrimaryKey = null;
    for (Field field : fields) {
      String fieldName = field.getName(), sqlName = null;
      if (notPrimaryKey == null && fieldName.equals(tableName.primaryKey())) {
        notPrimaryKey = false;
        sqlName = fieldName;
      }
      if (sqlName == null && field.isAnnotationPresent(TableField.class)) {
        TableField tableField = field.getAnnotation(TableField.class);
        if (tableField.exist()) {
          sqlName = tableField.value();
          if (Datas.isEmpty(sqlName)) {
            sqlName = fieldName.replaceAll("([A-Z])", "_$1").toLowerCase();
          }
          if (tableName.backtick() && sqlName.indexOf('`') != 0) {
            sqlName = String.format("`%s`", sqlName);
          }
        }
      }
      if (sqlName != null) {
        content.append(String.format("\n    final public WhereField<%s> %s = new WhereField<>(this, \"%s\");",
            whereName, fieldName, sqlName));
      }
    }
    if (notPrimaryKey == null) {
      throw new RuntimeException(clazz.getName() + " 缺少主键字段 " + tableName.primaryKey());
    }
    content.append("\n  }\n");
    content.append(content.toString().replaceAll("( | Base)Where", "$1Set").replaceAll(whereName, setName));
    sb.append(content);

    // 开始写服务;
    String nc = code;
    if (nc == null) {
      nc = String.format("package %s;\n\n"
          + "import org.springframework.stereotype.Service;\n\n"
          + importEntity
          + "import io.gitee.macxiang.sql.wapper.BaseService;\n\n"
          + "@Service\n"
          + "public class %s extends BaseService<%s, Auto.%s, Auto.%s> {\n"
          + "\n}\n",
          packageService,
          className, entityClassName,
          whereName, setName);
      list.add(new PathCode(pathService, nc));
    }
  }

  public static HashMap<String, String> entity2HashMap(Object entity, boolean insert) {
    Class<?> clazz = entity.getClass();
    TableName tableName = clazz.getAnnotation(TableName.class);
    if (tableName != null) {
      Field[] fields = clazz.getDeclaredFields();
      String primaryKey = null;
      HashMap<String, String> results = new HashMap<>();
      for (Field field : fields) {
        String fieldName = field.getName(), sqlName = null;
        TableField tableField = null;
        if (field.isAnnotationPresent(TableField.class)) {
          tableField = field.getAnnotation(TableField.class);
          if (tableField != null && tableField.exist()) {
            sqlName = tableField.value();
          }
        } else if (insert && primaryKey == null && fieldName.equals(tableName.primaryKey())) {
          primaryKey = sqlName = fieldName;
        }
        if (sqlName != null) {
          if (sqlName.isEmpty()) {
            sqlName = fieldName;
          }
          sqlName = sqlName.replaceAll("([A-Z])", "_$1").toLowerCase();
          if (tableName.backtick() && sqlName.indexOf('`') != 0) {
            sqlName = String.format("`%s`", sqlName);
          }
          try {
            Object o = field.get(entity);
            boolean isCustom = false;
            if (o == null) {
              FieldStrategy fs = tableField == null ? FieldStrategy.NULL
                  : (insert ? tableField.insertStrategy() : tableField.updateStrategy());
              switch (fs) {
                case FieldStrategy.CUSTOM:
                  if (tableField != null) {
                    o = insert ? tableField.insertCustom() : tableField.updateCustom();
                    isCustom = true;
                  }
                  break;
                case FieldStrategy.EMPTY_STRING:
                  o = "";
                  break;
                case FieldStrategy.ZERO:
                  o = 0;
                  break;
                case FieldStrategy.TIMESTAMP:
                  o = Times.time();
                  break;
                case FieldStrategy.TIME_ISO8601:
                  o = Times.timeISO8601();
                  break;
                case FieldStrategy.LONG:
                  o = Datas.getId();
                  break;
                case FieldStrategy.STR32:
                  o = Datas.sGetId32();
                  break;
                case FieldStrategy.STR36:
                  o = Datas.sGetId36();
                  break;
                case FieldStrategy.STR40:
                  o = Datas.sGetId40();
                  break;

                default:
                  break;
              }
            }
            if (o != null) {
              results.put(sqlName, isCustom ? o.toString() : SQL.safeJSON(o));
            }
          } catch (Exception e) {
          }
        }
      }
      return results;

    }
    return null;
  }

  public static List<SqlFieldMap> getSqlFieldMap(Class<?> clazz, HashMap<String, SqlFieldMap> outPrimary) {
    String primaryKey = clazz.getAnnotation(TableName.class).primaryKey();
    List<SqlFieldMap> fs = new ArrayList<>();
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
      String fieldName = field.getName(), sqlName = null;
      TableField tableField = null;
      boolean exist = false, select = false;
      if (field.isAnnotationPresent(TableField.class)) {
        tableField = field.getAnnotation(TableField.class);
        if (tableField != null) {
          exist = tableField.exist();
          select = tableField.select();
          if (exist || select) {
            sqlName = tableField.value();
            if (Datas.isEmpty(sqlName)) {
              sqlName = Datas.Hump2Underline(fieldName);
            }
          }
        }
      } else if (fieldName.equals(primaryKey)) {
        sqlName = Datas.Hump2Underline(fieldName);
      }
      if (sqlName != null) {
        if ((tableField == null || tableField.backtick()) && sqlName.indexOf('`') != 0) {
          sqlName = String.format("`%s`", sqlName);
        }

        String in = "DEFAULT", up = null;
        if (tableField != null) {
          switch (tableField.insertStrategy()) {
            case FieldStrategy.CUSTOM:
            case FieldStrategy.NONE:
              in = tableField.insertCustom();
              break;
            case FieldStrategy.EMPTY_STRING:
              in = "\"\"";
              break;
            case FieldStrategy.ZERO:
              in = "0";
              break;
            default:
              in = tableField.insertStrategy().toString();
              break;
          }
          switch (tableField.updateStrategy()) {
            case FieldStrategy.CUSTOM:
              up = tableField.updateCustom();
              break;
            case FieldStrategy.NONE:
              up = null;
              break;
            case FieldStrategy.EMPTY_STRING:
              up = "\"\"";
              break;
            case FieldStrategy.ZERO:
              up = "0";
              break;
            default:
              up = tableField.updateStrategy().toString();
              break;
          }
        }
        // 设置字段可访问
        field.setAccessible(true);
        SqlFieldMap t = new SqlFieldMap(sqlName, in, up, field, exist, select);
        fs.add(t);
        if (outPrimary != null && fieldName.equals(primaryKey)) {
          outPrimary.put("primary", t);
        }
      }

    }
    return fs;
  }

}
